Fix stack-dependent related_integrations.version export#6208
Conversation
…tions Replace find_least_compatible_version with find_compatible_version_range so prebuilt rules export the same related_integrations.version across stack backports. Bump version.lock for rules whose export changes.
Bug - GuidelinesThese guidelines serve as a reminder set of considerations when addressing a bug in the code. Documentation and Context
Code Standards and Practices
Testing
Additional Checks
|
…tions Replace find_least_compatible_version with find_compatible_version_range so prebuilt rules export the same related_integrations.version across stack backports. Bump pyproject.toml patch version. Resolves #5601
There was a problem hiding this comment.
Pull request overview
This PR makes related_integrations.version exports stack-invariant by replacing single stack-dependent compatibility anchors with an OR’d compatibility range derived from integration manifests.
Changes:
- Adds
find_compatible_version_rangeand supporting helpers for manifest-derived compatibility ranges. - Updates rule export conversion to use the new range and union policy templates across selected anchors.
- Replaces related integration tests and bumps the package version to
1.6.44.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
detection_rules/integrations.py |
Adds the new compatibility range computation and removes the old least-compatible helper. |
detection_rules/rule.py |
Uses the new range API when populating related_integrations. |
tests/test_integrations.py |
Updates unit tests for the new range behavior. |
pyproject.toml |
Bumps the project version. |
Iterate all majors overlapped by parsed Kibana bounds, derive legacy stack walk versions from manifest range floors instead of 8.19.0, remove a dead aligned-major branch, and drop RST-style double backticks in new docstrings.
…e-in-security_detection_engine-package-versions
Walk every stack major whose band intersects a bounded Kibana clause (e.g. >=8.12.0 <9.1.0 includes major 9) and pick the earliest compatible stack point within a major for the legacy least-compatible walk.
| effective_stack_majors = sorted( | ||
| stack_major for stack_major in stack_majors if stack_major >= max(stack_majors) - 1 | ||
| ) |
| for lo, hi in _parse_kibana_range(version_requirement): | ||
| majors_to_check: list[int] | ||
| if hi is None: | ||
| majors_to_check = [lo.major] |
There was a problem hiding this comment.
For this if check:
fix(integrations): tighten stack-major overlap and anchor resolution
Walk every stack major whose band intersects a bounded Kibana clause
(e.g. >=8.12.0 <9.1.0 includes major 9) and pick the earliest compatible
stack point within a major for the legacy least-compatible walk.
I don't think any of our manifests include a situation where hi is None unless one were to have a manifest with an unbounded range (e.g. ">=8.12.0"). Fine to have this check, but given this I am not sure that this functions as intended.
For the case with an unbounded range majors_to_check only will have [8] and not any other stack majors. Not sure if this accomplishes the goal.
Whereas for the case in the note, it correctly has multiple stack versions to check.
| package: str, | ||
| packages_manifest: dict[str, Any], | ||
| ) -> CompatibleVersionRange: | ||
| """Return a stack-invariant OR'd caret range for related_integrations.version. |
There was a problem hiding this comment.
Nit. Should this doc string be updated to fit the existing format? Short one sentence doc string with more detail in separate comment?
There was a problem hiding this comment.
+1 for this cleanup
| second = find_compatible_version_range("pkg", manifests) | ||
| self.assertEqual(first, second) | ||
|
|
||
| def test_single_major_appends_forward_anchor(self): |
There was a problem hiding this comment.
Nit. Akin to https://github.com/elastic/detection-rules/pull/6208/changes#r3325352838, if the goal is to support unbounded upper ranges like ">=8.12.0" should we include a test for that? The hi is none branch?
eric-forte-elastic
left a comment
There was a problem hiding this comment.
One main comment and a few nits. Otherwise LGTM 👍
Generally this PR is low risk, as the updated version computation logic in effect is not used in other workflows (like schema validation, etc.). Additionally, Kibana validates this to be a NonEmpty string just like we do in our dataclasses. (ref), so we have considerable freedom in what we populate it with.
shashank-elastic
left a comment
There was a problem hiding this comment.
This does not affect the release as such , but lock versions are impacted. Since we will handle that separately before the release ( either bump 1 across all or selectively bump 1 on affected rules), this PR should be good to merge.
Also checked CLI paths affected. It is working as expected.
python -m detection_rules dev integrations show-latest-compatible -p elastic_security -s 9.4.0
Loaded config file: /Users/shashankks/elastic_workspace/detection-rules/.detection-rules-cfg.json
█▀▀▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄ ▄ █▀▀▄ ▄ ▄ ▄ ▄▄▄ ▄▄▄
█ █ █▄▄ █ █▄▄ █ █ █ █ █ █▀▄ █ █▄▄▀ █ █ █ █▄▄ █▄▄
█▄▄▀ █▄▄ █ █▄▄ █▄▄ █ ▄█▄ █▄█ █ ▀▄█ █ ▀▄ █▄▄█ █▄▄ █▄▄ ▄▄█
Compatible integration version=('0.4.0', [''])
detection-rules on 5601-bug-improper-prebuilt-rule-version-usage-in-security_detection_engine-package-versions [$?] is 📦 v1.6.44 via 🐍 v3.12.8 (.venv) on ☁️ shashank.suryanarayana@elastic.co
❯ python -m detection_rules dev integrations show-latest-compatible -p pad -s 9.4.0
Loaded config file: /Users/shashankks/elastic_workspace/detection-rules/.detection-rules-cfg.json
█▀▀▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄ ▄ █▀▀▄ ▄ ▄ ▄ ▄▄▄ ▄▄▄
█ █ █▄▄ █ █▄▄ █ █ █ █ █ █▀▄ █ █▄▄▀ █ █ █ █▄▄ █▄▄
█▄▄▀ █▄▄ █ █▄▄ █▄▄ █ ▄█▄ █▄█ █ ▀▄█ █ ▀▄ █▄▄█ █▄▄ █▄▄ ▄▄█
Compatible integration version=('2.0.0', [''])
detection-rules on 5601-bug-improper-prebuilt-rule-version-usage-in-security_detection_engine-package-versions [$?] is 📦 v1.6.44 via 🐍 v3.12.8 (.venv) on ☁️ shashank.suryanarayana@elastic.co
❯ | package: str, | ||
| packages_manifest: dict[str, Any], | ||
| ) -> CompatibleVersionRange: | ||
| """Return a stack-invariant OR'd caret range for related_integrations.version. |
There was a problem hiding this comment.
+1 for this cleanup
…ranges Merge main (#6251) and port data-stream schema filtering into find_compatible_version_range instead of find_least_compatible_version. Keep find_latest_integration_patch_for_minor for ES|QL validation. Integration-specific export uses schema walk plus schema-floor fallback for streams gated behind newer package versions (e.g. aadgraphactivitylogs).
Fix formatting on integration_name assignment that failed ruff format --check.
Walk all stack majors for non-aligned packages, handle unbounded Kibana ranges, restore schema comments, use immutable anchor tuples, and add UNKNOWN_PACKAGE_INTEGRATION constant with regression tests.
Prebuilt rules ship to the stack lines in stack-schema-map, not every Kibana major a manifest ever claimed. Filter against get_stack_versions() so dead lines like Kibana 7.x do not pull azure 0.0.2 into related_integrations.
Remove aligned-major fast path, redundant overlap checks, and dead branches from the #5601 OR-range export. Cache shipped stack majors, derive unbounded-range test expectations from _MAX_UNBOUNDED_STACK_MAJOR_SPAN, and inline schema-floor fallback at its single call site.
| # Cap stack majors collected from an unbounded Kibana clause (``>=X.Y.Z``). EPR caret/tilde | ||
| # ranges are always bounded today; this only applies if EPR ever emits an open-ended requirement. | ||
| _MAX_UNBOUNDED_STACK_MAJOR_SPAN = 10 |
| schemas = load_integrations_schemas() | ||
| manifests = load_integrations_manifests() | ||
| result = find_compatible_version_range("azure", manifests, integration="aadgraphactivitylogs") | ||
| self.assertIn("1.37.0", result.anchors) | ||
| self.assertNotIn("1.0.0", result.anchors) | ||
| self.assertNotIn("0.0.2", result.anchors) | ||
| self.assertIn("^1.37.0", result.range) | ||
| self.assertEqual(result.range, "^1.37.0 || ^2.0.0") | ||
| floor_versions = [ | ||
| version | ||
| for version in sorted(schemas["azure"], key=Version.parse) | ||
| if "aadgraphactivitylogs" in schemas["azure"][version] | ||
| ] | ||
| self.assertEqual(floor_versions[0], "1.37.0") |
Skip the metadata-only package entry when the ES|QL query already names a data stream for that package (e.g. azure.signinlogs). Kuery/EQL paths are unchanged. Adds regression tests for azure signinlogs and aadgraph.
Remove file-path-based related_integrations tests that depend on local-only rule TOMLs and live manifest output. Keep the pure helper coverage for _esql_metadata_package_row_needed.
Hoist _integration_schema_floor() to remove duplicate minimum_schema calls in find_compatible_version_range. Shorten export-path comments after audit; no behavior change.
Collect related_integrations anchors from each get_stack_versions() entry instead of synthesizing stack-major floors (e.g. 9.0.0). AWS 5.x/6.x require Kibana ^9.2+ and were missed, causing false version-mismatch warnings on 9.2+.
…ckage Skip redundant related_integrations rows for endpoint, windows, and other NON_DATASET_PACKAGES when the query already references package.* datasets. Extends ES|QL metadata dedupe to EQL/KQL rules.
…e-in-security_detection_engine-package-versions






Issue link(s): Resolves #5601
Summary - What I changed
This PR fixes several bugs in how
related_integrations.versionis computed at export time. All of them affected the value written into prebuilt rules; none change ES|QL validation (that still usesfind_latest_compatible_version/ patch inference from #6251). Addressing the initial bug is a smaller PR, but through testing, we identified several other issues predominantly with ESQL (given we do not have an ESQL parser), that expands the scope of the changes.Bugs fixed
Stack-dependent export ([Bug] Improper prebuilt rule version usage in security_detection_engine package versions #5601).
find_least_compatible_versionpicked a single^X.Y.Zfrom the build-time stack version. The same rule id could ship^8.2.0on an 8.19 backport and^9.0.0on 9.x while the prebuilt ruleversioninteger stayed the same, which triggered false integration version-mismatch warnings after upgrade.Schema-blind OR ranges. Rebased onto [Bug] ESQL Remote Validation Data Stream and Patch Version Validation #6251 so export respects
integration-schemas.json.gz: data streams introduced mid-package (for exampleaadgraphactivitylogsin azure 1.37.0) get a schema floor in the OR range, not just Kibana manifest compatibility.Phantom anchors from unshipped stack lines. Walking every Kibana major in the manifest could emit legacy anchors such as
^0.0.2from azure packages that still declare^7.9.0. Export now intersects with stack majors from the stack-schema-map backport lines we actually ship rules to.Incomplete stack-major coverage. Bounded ranges like
>=8.12.0 <9.1.0omitted major 9; unbounded>=ranges stopped at the lower bound only. Fixed with per-clause major overlap and a cap for open-ended ranges.Duplicate ES|QL
related_integrationsrows. ES|QL rules tagged[metadata] integration = ["azure"]with a query filter onazure.signinlogs(or similar) got both a spurious metadata-only package row and the data-stream row. Kuery/EQL were unaffected. Fixed with a prefix check so the metadata row is skipped when the query already names a dataset for that package.Implementation
find_least_compatible_versionwithfind_compatible_version_range, which returns a stack-invariant OR'd caret range (for example^8.2.0 || ^9.0.0 || ^10.0.0for endpoint) plus a forward sentinel for the next integration major.policy_templatesare unioned across manifest-backed anchors, not parsed from the OR string.UNKNOWN_PACKAGE_INTEGRATIONconstant; tuple anchors onCompatibleVersionRange.Performance: the new path resolves more stack lines per package. Informal local timing was roughly 2 to 9x median vs the old function on
endpoint/aws/windowsmanifests, still sub-10ms per call and once per rule at package build. Not expected to movebuild-releasewall time in a meaningful way.Follow-up:
version.lockbumps for affected rules are planned separately before the next package release (per review).How To Test
Spot check export: rule
8d366588-cbd6-43ba-95b4-0971c3f906e5should show^8.2.0 || ^9.0.0 || ^10.0.0for endpoint on every release line.Export toml and import into kibana to ensure it applies successfully.
20260528T154018L.ndjson.txt
Backport validation: cherry-pick this PR onto each release branch (or apply the changed files from the branch), then run the same pytest commands. No
version.lockchanges required for that pass.Backport `8.19` (origin/8.19 + PR code)
Backport `9.2` (origin/9.2 + PR code)
Backport `9.3` (origin/9.3 + PR code)
Backport `9.4` (origin/9.4 + PR code)
Checklist
bugmeta:rapid-mergelabel if planning to merge within 24 hours